package com.sunlee.admin.api.util;


import com.github.junrar.Archive;
import com.github.junrar.exception.UnsupportedRarV5Exception;
import com.github.junrar.rarfile.FileHeader;
import com.sunlee.admin.api.constant.StringConstant;
import com.sunlee.common.core.exception.BusinessException;
import com.sunlee.common.service.enums.FileTypeEnum;
import org.apache.commons.io.FileUtils;

import java.io.*;
import java.nio.charset.Charset;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

/**
 * @author sunlee
 * @date 2023-03-26
 */
public class DecompressUtil {

    public static void main(String[] args) {
        String packagePath = "D:\\小鸡PPT\\az.zip";
        StringBuilder pptPathBuilder = new StringBuilder();
        pptPathBuilder.append("D:\\小鸡PPT\\你好呀");
        try {
            decompressUtilWithPpt(packagePath, pptPathBuilder);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static boolean decompressUtilWithPpt(String packagePath, StringBuilder pptPathBuilder) throws Exception {

        File packagePathFile = new File(packagePath);
        FileTypeEnum fileType = getFileType(packagePathFile);

        // ZIP
        if (FileTypeEnum.ZIP.equals(fileType)) {
            return unZip(packagePathFile, pptPathBuilder);
        }

        // RAR
        if (FileTypeEnum.RAR.equals(fileType)) {
            return unRar(packagePathFile, pptPathBuilder);
        }

        throw new BusinessException("错误文件类型：" + fileType);
    }

    private static boolean unZip(File zipFile, StringBuilder pptPathBuilder) throws Exception {
        boolean result = false;

        try (FileInputStream input = new FileInputStream(zipFile);
             ZipInputStream zipInputStream = new ZipInputStream(new BufferedInputStream(input), Charset.forName("GBK"));
        ) {

            int existFileNum = 0;
            ZipEntry ze;
            while ((ze = zipInputStream.getNextEntry()) != null) {

                // 判断是否存在ppt文件
                if (ze.getName().endsWith(StringConstant.PPTX)) {
                    pptPathBuilder.append(StringConstant.PPTX);
                } else if (ze.getName().endsWith(StringConstant.PPT)) {
                    pptPathBuilder.append(StringConstant.PPT);
                } else {
                    existFileNum++;
                    continue;
                }

                // 开始解压
                FileUtils.copyInputStreamToFile(zipInputStream, new File(pptPathBuilder.toString()));
                result = true;
                break;
            }

            if (!result && existFileNum > 0) {
                throw new BusinessException("压缩包不存在PPT文件");
            }
        }

        return result;
    }

    /**
     * 解压缩RAR文件
     *
     * @param zipFile        压缩包文件
     * @param pptPathBuilder 目标文件夹
     */
    private static boolean unRar(File zipFile, StringBuilder pptPathBuilder) throws Exception {
        boolean result = false;

        try (Archive archive = new Archive(zipFile)) {

            FileHeader fileHeader;
            int existFileNum = 0;

            while ((fileHeader = archive.nextFileHeader()) != null) {

                // 判断是否存在ppt文件
                if (fileHeader.getFileName().endsWith(StringConstant.PPTX)) {
                    pptPathBuilder.append(StringConstant.PPTX);
                } else if (fileHeader.getFileName().endsWith(StringConstant.PPT)) {
                    pptPathBuilder.append(StringConstant.PPT);
                } else {
                    existFileNum++;
                    continue;
                }
                try (OutputStream outputStream = new FileOutputStream(pptPathBuilder.toString());) {
                    archive.extractFile(fileHeader, outputStream);
                }
                result = true;
                break;
            }
            if (!result && existFileNum > 0) {
                throw new BusinessException("压缩包不存在PPT文件");
            }
        } catch (UnsupportedRarV5Exception e) {
            throw new BusinessException("不支持RarV5压缩包");
        }
        return result;
    }

    /**
     * 获取文件真实类型
     *
     * @param file 要获取类型的文件。
     * @return 文件类型枚举。
     */
    private static FileTypeEnum getFileType(File file) {
        FileInputStream inputStream = null;
        try {
            inputStream = new FileInputStream(file);
            byte[] head = new byte[4];
            if (-1 == inputStream.read(head)) {
                return FileTypeEnum.UNKNOWN;
            }
            int headHex = 0;
            for (byte b : head) {
                headHex <<= 8;
                headHex |= b;
            }
            switch (headHex) {
                case 0x504B0304:
                    return FileTypeEnum.ZIP;
                case 0x776f7264:
                    return FileTypeEnum.TAR;
                case -0x51:
                    return FileTypeEnum._7Z;
                case 0x425a6839:
                    return FileTypeEnum.BZ2;
                case -0x74f7f8:
                    return FileTypeEnum.GZ;
                case 0x52617221:
                    return FileTypeEnum.RAR;
                default:
                    return FileTypeEnum.UNKNOWN;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return FileTypeEnum.UNKNOWN;
    }

    /**
     * 构建目录
     *
     * @param outputDir 输出目录
     * @param subDir    子目录
     */
    private static void createDirectory(String outputDir, String subDir) {
        File file = new File(outputDir);
        //子目录不为空
        if (!(subDir == null || "".equals(subDir.trim()))) {
            file = new File(outputDir + File.separator + subDir);
        }
        if (!file.exists()) {
            if (!file.getParentFile().exists()) {
                file.getParentFile().mkdirs();
            }
            file.mkdirs();
        }
    }
}
